/*
 * Triangle.java
 *
 * Created on September 22, 2007, 9:07 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package flightlifter;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;

public class Triangle extends Intersectable {

    protected static final boolean DEBUG = true;
    public float area = 0;
    public Texture texture;
    public PVector vert0, vert1, vert2;
    PVector edge1, edge2;
    public float u0, u1, u2, v0, v1, v2;
    public float uspan = 0, vspan = 0;
    public float minu, minv;
    public PVector gNormal = new PVector();
    int displayListIndex = 0;

    public Triangle(Shader shader, PVector vert0, PVector vert1, PVector vert2, float u0, float v0, float u1, float v1, float u2, float v2, Texture texture) {

        this.shader = shader;
        this.vert0 = vert0;
        this.vert1 = vert1;
        this.vert2 = vert2;

        //precalculate the edges
        edge1 = PVector.sub(vert1, vert0);
        edge2 = PVector.sub(vert2, vert0);

        this.u0 = u0;
        this.u1 = u1;
        this.u2 = u2;

        this.v0 = v0;
        this.v1 = v1;
        this.v2 = v2;

        this.texture = texture;

        minBounds = new PVector();
        maxBounds = new PVector();

        //get the bounds of this triangle
        minBounds.x = Util.min(vert0.x, vert1.x, vert2.x);
        minBounds.y = Util.min(vert0.y, vert1.y, vert2.y);
        minBounds.z = Util.min(vert0.z, vert1.z, vert2.z);

        maxBounds.x = Util.max(vert0.x, vert1.x, vert2.x);
        maxBounds.y = Util.max(vert0.y, vert1.y, vert2.y);
        maxBounds.z = Util.max(vert0.z, vert1.z, vert2.z);


        //calculate geometric normal
        edge2.cross(edge1, gNormal);
        gNormal.normalize();

        PVector temp = new PVector();
        edge1.cross(edge2, temp);
        area = .5f * temp.mag();

        float maxu = Util.max(u0, u1, u2);
        minu = Util.min(u0, u1, u2);
        float maxv = Util.max(v0, v1, v2);
        minv = Util.min(v0, v1, v2);

        uspan = maxu - minu;
        vspan = maxv - minv;
        buildDL();


    }

    private void buildDL() {
        displayListIndex = GL11.glGenLists(1);
        GL11.glNewList(displayListIndex, GL11.GL_COMPILE);
        render();
        GL11.glEndList();
    }

    public void draw() {
        GL11.glCallList(displayListIndex);
    }

    public void render() {
        
        if (texture == null) {
        GL11.glColor3f(shader.fillColor.x, shader.fillColor.y, shader.fillColor.z);
        GL11.glBegin(GL11.GL_TRIANGLES);
        GL11.glVertex3f(vert0.x, vert0.y, vert0.z);
        GL11.glVertex3f(vert1.x, vert1.y, vert1.z);
        GL11.glVertex3f(vert2.x, vert2.y, vert2.z);
        GL11.glEnd();
        } else {
        
         
        GL20.glUseProgram(0);
        GL11.glColor3f(1, 1, 1);
        texture.bind();

        GL11.glBegin(GL11.GL_TRIANGLES);

        GL11.glTexCoord2f(u0, v0);
        GL11.glVertex3f(vert0.x, vert0.y, vert0.z);

        GL11.glTexCoord2f(u1, v1);
        GL11.glVertex3f(vert1.x, vert1.y, vert1.z);

        GL11.glTexCoord2f(u2, v2);
        GL11.glVertex3f(vert2.x, vert2.y, vert2.z);

        GL11.glEnd();
        }

    }

    public boolean intersect(Ray ray) {

        PVector pVec = new PVector(), tVec, qVec = new PVector();

        float det, inv_det;

        ray.dir.cross(edge2, pVec);

        det = edge1.dot(pVec);

        //discard if parallel to plane
        if (det > -.00001 && det < .00001) {
            return false;
        }
        inv_det = 1.f / det;


        tVec = PVector.sub(ray.origin, vert0);


        ray.u = tVec.dot(pVec) * inv_det;
        if (ray.u < 0.0 || ray.u > 1.0) {
            return false;
        }


        tVec.cross(edge1, qVec);


        ray.v = ray.dir.dot(qVec) * inv_det;

        if (ray.v < 0.f || ray.u + ray.v > 1.f) {
            return false;
        }


        ray.distance = edge2.dot(qVec) * inv_det;


        if (ray.distance > ray.maxDist || ray.distance < ray.minDist) {
            return false;
        }


        //generate texture coordinates

        float b = 1.0f - ray.u - ray.v;
        ray.tu = (u0 * b + u1 * ray.u + u2 * ray.v);
        ray.tv = (v0 * b + v1 * ray.u + v2 * ray.v);

        //state.tangent1 = edge1;
        //edge1.cross(state.normal,state.tangent2);
        return true;

    }

    /**
     * Gets the point in space occupied by a location on the texture surface
     *
     * @param u u coord of texture point
     * @param v v coord of texture point
     * @return 
     */
    public PVector getPoint(float u, float v) {

        float b = 1.f - u - v;

        return new PVector(vert0.x + v * edge2.x + u * edge1.x,
                vert0.y + v * edge2.y + u * edge1.y,
                vert0.z + v * edge2.z + u * edge1.z);
    }

    public boolean inside(BoundingBox bounds) {

        return ((greaterThan(maxBounds, bounds.min)) && (lessThan(minBounds, bounds.max)));
    }

    private boolean greaterThan(PVector lhs, PVector rhs) {
        if (lhs.x >= rhs.x && lhs.y >= rhs.y && lhs.z >= rhs.z) {
            return true;
        } else {
            return false;
        }
    }

    private boolean lessThan(PVector lhs, PVector rhs) {
        if (lhs.x <= rhs.x && lhs.y <= rhs.y && lhs.z <= rhs.z) {
            return true;
        } else {
            return false;
        }
    }
}
